package parsedata

import (
	"encoding/csv"
	"fmt"
	"log"
	"math"
	"os"
	"strconv"

	"gonum.org/v1/gonum/mat"
)

func ParseCSV(path string) (*mat.Dense, []string, error) {
	matrix, names, err := fileToMatrix2(path)
	if err != nil {
		return nil, nil, err
	}
	log.Println("Размерность данных до первичной обработки: ", matrix.RawMatrix().Rows, "x", matrix.RawMatrix().Cols)
	cleanMatrix(matrix)
	log.Println("Размерность данных после удаления неполных строк: ", matrix.RawMatrix().Rows, "x", matrix.RawMatrix().Cols)
	return matrix, names, nil
}

func fileToMatrix(path string) (*mat.Dense, []string, error) {
	file, err := os.Open(path)
	if err != nil {
		log.Println(err)
		return nil, nil, err
	}
	defer file.Close()

	reader := csv.NewReader(file)
	records, err := reader.ReadAll()
	if err != nil {
		log.Println(err)
		return nil, nil, err
	}

	names := records[0]

	records = records[1:]

	// преобразование в матрицу

	rows, cols := len(records), len(records[0])
	data := make([]float64, rows*cols)

	brands := make(map[string]int, 0)
	countsBrands := 0

	for i, record := range records {
		for j, strValue := range record {
			if j == 0 {
				v, res := brands[strValue]
				if !res {
					brands[strValue] = countsBrands
					data[i*cols+j] = float64(countsBrands)
					countsBrands++
				} else {
					data[i*cols+j] = float64(v)
				}
				continue
			}

			value, err := strconv.ParseFloat(strValue, 64)
			if err != nil {
				if i*j != 0 {
					log.Println("Record: ", i, " Column: ", j, "\n", err)
				}
				data[i*cols+j] = math.NaN()
			}
			data[i*cols+j] = value
		}
	}

	//data = data[:10]
	//fmt.Println(data)

	matrix := mat.NewDense(rows, cols, data)
	return matrix, names, nil
}

func fileToMatrix2(path string) (*mat.Dense, []string, error) {
	file, err := os.Open(path)
	if err != nil {
		log.Println(err)
		return nil, nil, err
	}
	defer file.Close()

	reader := csv.NewReader(file)
	records, err := reader.ReadAll()
	if err != nil {
		log.Println(err)
		return nil, nil, err
	}

	names := records[0]

	records = records[1:]

	records = delNaNRows(records, "?")

	fmt.Println(records, "\n\n", len(records))

	// преобразование в матрицу

	rows, cols := len(records), len(records[0])
	data := make([]float64, rows*cols)

	//var vals []map[string]int
	vals := make([]map[string]int, len(names))
	for i := range vals {
		vals[i] = make(map[string]int)
	}

	for i, record := range records {
		for j, strValue := range record {

			_, res := vals[j][strValue]
			if !res {
				vals[j][strValue] = len(vals[j])
			}
			data[i*cols+j] = (float64(vals[j][strValue]))
		}
	}

	//fmt.Println(len(names), len(records[0]), records[0][16])

	//data = data[:10]
	//fmt.Println(data)

	matrix := mat.NewDense(rows, cols, data)
	//fmt.Println(matrix)
	return matrix, names, nil
}

func cleanMatrix(matrix *mat.Dense) {
}

func delNaNRows(m [][]string, nanVal string) [][]string {
	cols, rows := len(m[0]), len(m)

	for i := 0; i < rows; i++ {
		for j := 0; j < cols; j++ {
			if m[i][j] == nanVal {
				m = append(m[:i], m[i+1:]...)
				i--
				rows--
				break
			}
		}
	}

	return m
}
